home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 21 / Cream of the Crop 21 (Terry Blount) (October 1996).iso / os2 / vsoup11.zip / news.cc < prev    next >
C/C++ Source or Header  |  1996-09-02  |  43KB  |  1,697 lines

  1. //  $Id: news.cc 1.23 1996/09/02 13:25:10 hardy Exp $
  2. //
  3. //  This progam/module was written by Hardy Griech based on ideas and
  4. //  pieces of code from Chin Huang (cthuang@io.org).  Bug reports should
  5. //  be submitted to rgriech@ibm.net.
  6. //
  7. //  This file is part of soup++ for OS/2.  Soup++ including this file
  8. //  is freeware.  There is no warranty of any kind implied.  The terms
  9. //  of the GNU Gernal Public Licence are valid for this piece of software.
  10. //
  11. //  Get news from NNTP server.
  12. //
  13. //  rg270596:
  14. //  - multithreading support for OS/2
  15. //
  16. //  Und nun mal ein paar Erfahrungen mit multithreading (in diesem Zusammenhang):
  17. //  -----------------------------------------------------------------------------
  18. //
  19. //  Probleme mit CHANGI:
  20. //  - die ursprüngliche CHANGI Version "spann" bei vielfachem Zugriff
  21. //    Update nach changi09m brachte erhebliche Erleichterung
  22. //  - nntp-NEXT tut nicht, wenn gleichzeitig ein Artikel gelesen wird,
  23. //    oder wenn mehrere NEXTs unterwegs sind (könnte CHANGI-Problem sein)
  24. //    120696:  war eher meine Blödheit und hatte u.U. was mit den Signalen zu tun...
  25. //             Wahrscheinlich braucht der NEXT recht lange und der entsprechende
  26. //             gets() wurde dann mit hoher Wahrscheinlichkeit durch ein Signal
  27. //             unterbrochen...
  28. //
  29. //  Probleme mit EMX-GCC09a:
  30. //  - das signal-handling scheint fragwürdig (signal muß aber abgefangen werden...):
  31. //    - nextchar in socket (wird durch recv/read gemacht) kommt u.U. mit EINTR
  32. //      zurück - doch wie setze ich dann wieder auf???
  33. //    - _beginthread kommt u.U. mit EINVAL zurück, was jedoch ebenso auf einen
  34. //      unterbrochenen Aufruf schließen läßt (d.h. Fehlerauswertung nicht vollständig)
  35. //    --> Signale nicht dazu verwenden, um die Beendigung eines Threads anzuzeigen,
  36. //        sondern nur in absoluten Notfällen!!!
  37. //  - einmal (?) hatte ich im (durch Semaphor geschützten) StdOut einen größeren
  38. //    Block doppelt.  Das Programm hat den Block unmöglich (?) produzieren können,
  39. //    also kommt nur EMX-GCC in Frage bzw. das OS
  40. //  - new/delete können nicht selbst definiert werden
  41. //  - wie komme ich bitte an _threadid ? (stddef.h war nicht für C++)
  42. //  - unlink steht nicht in stdio.h, sondern unistd.h
  43. //  - tmpfile() / tempnam() durch Semaphor geschützt ??
  44. //  --> hätte ich Zugriff, würde ich sofort nach 09b updaten!!!
  45. //
  46. //  Hausgemachte Probleme:
  47. //  - ein mehrfacher Request von einem MutexSemaphor in EINEM Thread hält diesen
  48. //    NICHT an.  Nur ein anderer Thread kann das Semaphor nicht mehr anfordern...
  49. //  - Zustandsmaschine war durch 'mode reader' nicht mehr korrekt (es wurde schon ein
  50. //    'waiting' angezeigt, obwohl noch 'init' war...)
  51. //    - in nntpMtGetFinished wurde der Zustand zweimal abgefragt und dann noch in der
  52. //      Reihenfolge 'finished'?, 'running'?.  Dieser Übergang wird aber in einem Thread
  53. //      gemacht -> Thread war u.U. noch nicht 'finished', aber auch nicht mehr 'running'.
  54. //      Dies ergibt ein leicht inkonsistentes Bild der Zustände!
  55. //  - wird ein Zähler im Thread hochgezählt und muß hinterher ausgewertet
  56. //    werden, so empfiehlt sich mindestens ein Semaphor (vielleicht auch noch
  57. //    volatile) (bytesRcvd)
  58. //  - die Threads müssen auch einen Signal-Handler für z.B. SIGPIPE haben, sonst
  59. //    gibt es bei Abbruch u.U. einen doppelten Fehler! (das kommt daher, wie ein
  60. //    Programm abgebrochen wird)
  61. //  - ein Event-Semaphor will auch zurückgesetzt werden!  Die 'Kinder' laufen sonst
  62. //    echt Gefahr zu verhungern...
  63. //  - stream-I/O muß konsequent durch MuxSema geschützt werden (ein bißchen Disziplin
  64. //    bitte)
  65. //  - regexp hat statische Variablen
  66. //  - um Klassen, die was mit Listen oder so zu tun haben, am besten auch ein
  67. //    individuelles Semaphor legen
  68. //  - stimmt der makefile nicht, und es wird ein Datentyp geändert, so kommt es
  69. //    klarerweise zu seltsamen Effekten (die Objekte werden nicht neu angelegt, etc.)
  70. //  - beachte Zuweisung eines 'char' von 0xff (== -1) an einen int!!!
  71. //
  72.  
  73.  
  74. //
  75. //  um das Signal-Problem hinzubekommen einfach definieren!
  76. //  - nextchar() in socket liefert bei read bzw recv EINTR zurück
  77. //  - _beginthread wird u.U. auch unterbrochen (recht unwahrscheinlich, aber wahr)
  78. //
  79. //#define SIGNAL_PROBLEM
  80.  
  81. //
  82. //  Testroutine, die die Angelegenheit ein bißchen behindern soll, einbinden
  83. //
  84. //#define INSERT_MTTEST
  85.  
  86.  
  87. #include <assert.h>
  88. #include <process.h>
  89. #include <signal.h>
  90. #include <stdio.h>
  91. #include <stdlib.h>
  92. #include <string.h>
  93. #include <time.h>
  94.  
  95. #include "areas.hh"
  96. #include "global.hh"
  97. #include "kill.hh"
  98. #include "mts.hh"
  99. #include "news.hh"
  100. #include "newsrc.hh"
  101. #include "nntp.hh"
  102. #include "nntpcl.hh"
  103. #include "socket.hh"
  104.  
  105.  
  106.  
  107. static TSemaphor   divSema;
  108. static TEvSemaphor threadFinito;
  109. static TEvSemaphor disconnectDone;
  110.  
  111. static TProtCounter artsRcvd;         // articles received...
  112. static TProtCounter artsKilled;       // articles killed...
  113.  
  114. static TKillFile killF;               // kill file handling
  115.  
  116. static TProtCounter getArt_artRcvd;   // globals for mtGetArticle()
  117. static long getArt_grpHi;             //    (I hate those f* globals -> restructure)
  118. static int doingProcessSendme;        //    currently doing processSendme
  119.  
  120. static long catchupNumKeep;
  121.  
  122. //
  123. //  thread states (init must be 0)
  124. //  starting is for debugging and not absolutely required...
  125. ////  (are static vars initialized to zero ?)
  126. //
  127. enum NntpStates { init,connecting,failed,waiting,starting,running,runningspecial };
  128.  
  129. //
  130. //  these are the thread connections to the news server
  131. //
  132. #ifndef INSERT_MTTEST
  133. static TNntp nntp[ MAXNNTPTHREADS ];             // no pointer (because of imlicit destructor)
  134. static NntpStates nntpS[MAXNNTPTHREADS ] = {init};
  135. #else
  136. static TNntp nntp[ MAXNNTPTHREADS+1 ];
  137. static NntpStates nntpS[MAXNNTPTHREADS+1 ] = {init };
  138. #define TESTTHREAD (MAXNNTPTHREADS)
  139. #endif
  140.  
  141.  
  142.  
  143. //--------------------------------------------------------------------------------
  144. //
  145. //  utility functions
  146. //
  147.  
  148.  
  149.  
  150. #ifdef SIGNAL_PROBLEM
  151. static void signalHandler( int sig )
  152. //
  153. //  nur dazu da, um sleep abzubrechen
  154. //
  155. {
  156.     signal( sig, SIG_ACK );
  157. }   // signalHandler
  158. #endif
  159.  
  160.  
  161.  
  162. #if defined(DEBUG)  ||  defined(TRACE_ALL)  ||  defined(TRACE)  ||  defined(TRACE_ALL)
  163. static void printThreadState( const char *pre, int maxNo=maxNntpThreads )
  164. {
  165.     int i;
  166.     char b[100];
  167.  
  168.     assert( maxNo <= maxNntpThreads );
  169.  
  170.     sprintfT( b,"%s: ",pre );
  171.     for (i = 0;  i < maxNo;  i++) {
  172.     switch (nntpS[i]) {
  173.     case init:
  174.         strcat(b,"[i]");
  175.         break;
  176.     case connecting:
  177.         strcat(b,"[c]");
  178.         break;
  179.     case starting:
  180.         strcat(b,"[s]");
  181.         break;
  182.     case waiting:
  183.         strcat(b,"[w]");
  184.         break;
  185.     case running:
  186.         strcat(b,"[r]");
  187.         break;
  188.     case runningspecial:
  189.         strcat(b,"[R]");
  190.         if (maxNo < maxNntpThreads)
  191.         ++maxNo;
  192.         break;
  193.     case failed:
  194.         strcat(b,"[E]");
  195.         if (maxNo < maxNntpThreads)
  196.         ++maxNo;
  197.         break;
  198.     }
  199.     }
  200.     printfT( "%s\n",b );
  201. }
  202. #endif
  203.  
  204.  
  205.  
  206. static int killArticleQ( const char *groupName, const char *headerLine )
  207. //
  208. //  this is a hook function for TNntp...
  209. //
  210. {
  211.     return killF.matchLine( groupName,headerLine );
  212. }   // killArticleQ
  213.  
  214.  
  215.  
  216. static void processXref( const char *s )
  217. //
  218. //  Process an Xref line.
  219. //  format: 'Xref: '<host-name> <grp-name[: ]grp-num>(\b<grp-name[: ]grp-num>)*
  220. //  - s points behind 'Xref: '
  221. //  - \b may be blank or \t
  222. //  
  223. //  rg260596:  the new version works with sscanf (before strtok).  Hopefully this version
  224. //             is ok for multithreading
  225. //
  226. //  hook function for TNntp...
  227. //
  228. //
  229. {
  230.     const char *p, name[FILENAME_MAX];
  231.     int num, cnt;
  232.  
  233. #ifdef DEBUG_ALL
  234.     printfT( "XREF: '%s'\n",s );
  235. #endif
  236.  
  237.     //
  238.     //  Skip the host field
  239.     //
  240.     p = strpbrk( s," \t" );
  241.     if (p == NULL)
  242.     return;
  243.  
  244.     //
  245.     //  Look through the rest of the fields
  246.     //  (note:  the %n does not count in the sscanf-result)
  247.     //
  248.     while (sscanfT(p,"%*[ \t]%[^ \t:]%*[ \t:]%d%n",name,&num,&cnt) == 2) {
  249. #ifdef DEBUG_ALL
  250.     printfT( "xref: '%s' %d\n",name,num )